package de.scs.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.border.EtchedBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

import de.scs.beans.InsulinPump;
import de.scs.constants.HardwareCheckConstants;
import de.scs.core.BloodSugarParser;
import de.scs.core.HealthStatusController;
import de.scs.core.HealthStatusController.BSLTask;

public class EventGenerator extends JPanel implements ActionListener, FocusListener, KeyListener
{

  /**
     * 
     */
  private static final long     serialVersionUID = 4770431106554747028L;

  private InsulinPump           insulinPump;
  private static final String[] borderTitles     =
                                                 { "Set Time Period", "Pump Device Control", "Set Blood Sugar Level" };
  private JToggleButton         battery;
  private JToggleButton         insulin;
  private List<JTextField>      textFields;
  private JComboBox             pumpComponents;
  private boolean               isSent           = false;
  private boolean               fileSelected     = false;
  private static boolean        timerRunning;
  private JFileChooser          fc;
  private JLabel                fcPathLabel;
  private File                  file;
  private JTextField            bsvField;
  private BloodSugarParser      parser;
  private java.util.Timer       timer            = null;
  private JButton               clearButton;
  private JTextField            pom;
  private BSLTask               task;
  private JButton               openButton;
  private JButton               sendButton;

  public EventGenerator(InsulinPump insulinPump)
  {
    this.insulinPump = insulinPump;
    parser = new BloodSugarParser();
    textFields = new ArrayList<JTextField>();
    BSLTask.addPropertyChangeListener(new PropertyChangeListener()
    {
      @Override
      public void propertyChange(PropertyChangeEvent e)
      {
        if (e.getPropertyName().equals("stopped"))
        {
          timerRunning = (Boolean) e.getNewValue();
          if (timerRunning)
          {
            resetComponentsOnNoActiveTimer();
          }
        }
      }
    });
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    setPreferredSize(new Dimension(100, 40));
    createGeneratorUI();
  }

  private void createGeneratorUI()
  {
    JPanel top = getJPanelWithGridLayout(2, 3, 100, 100);
    top = getTitledBorder(top, borderTitles[0]);
    top.add(new JLabel("Period of Measurement"));
    pom = getJTextField("pom", "possible values: 20 - 600");
    top.add(pom);
    top.add(new JLabel("sec"));
    top.add(new JLabel("Period of Hardware Check"));
    JTextField pohc = getJTextField("pohc", "possible values: 20 - 600");
    top.add(pohc);
    top.add(new JLabel("sec"));
    add(top);

    JPanel mid = getJPanelWithGridLayout(1, 2, 100, 80);
    mid = getTitledBorder(mid, borderTitles[1]);
    mid.add(new JLabel("(De-)Activate Device"));
    pumpComponents = new JComboBox(HardwareCheckConstants.HARDWARE_DEFECTIVE);
    pumpComponents.addKeyListener(this);
    pumpComponents.setPreferredSize(new Dimension(120, 20));
    mid.add(pumpComponents);
    add(mid);

    JPanel bottom = getJPanelWithGridLayout(3, 1, 100, 150);
    bottom = getTitledBorder(bottom, borderTitles[2]);
    FlowLayout bsvLayout = new FlowLayout();
    bsvLayout.setAlignment(FlowLayout.LEADING);
    JPanel bsvPanel = new JPanel(bsvLayout);
    JLabel bsvLabel = new JLabel("Blood Sugar Value");
    bsvLabel.setPreferredSize(new Dimension(160,20));
    bsvPanel.add(bsvLabel);
    bsvField = getJTextField("bsv", "possible values: 20 - 250");
    bsvPanel.add(bsvField);
    bsvPanel.add(new JLabel("mg/dl"));
    bottom.add(bsvPanel);
    
    JPanel fcLabelPanel = getJPanelWithGridLayout(1, 2, 100, 30);
    JLabel fcLabel = new JLabel(" Selected Path:");
    fcLabel.setPreferredSize(new Dimension(100,30));
    fcPathLabel = new JLabel();
    fcLabelPanel.add(fcLabel);
    fcLabelPanel.add(fcPathLabel);
    bottom.add(fcLabelPanel);
    JPanel fcPanel = getJPanelWithGridLayout(1, 2, 100, 30);
    openButton = new JButton("Open File...", new ImageIcon(getClass().getResource("/resources/icons/open.gif")));
    openButton.setActionCommand("fc");
    openButton.setPreferredSize(new Dimension(50, 30));
    openButton.addActionListener(this);
    clearButton = new JButton("Clear File");
    clearButton.setPreferredSize(new Dimension(50, 30));
    clearButton.setActionCommand("clear");
    clearButton.addActionListener(this);
    fcPanel.add(openButton);
    fcPanel.add(clearButton);
    fc = new JFileChooser(".");
    fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
    bottom.add(fcPanel);
    
    add(bottom);

    JPanel buttonPanel = getJPanelWithGridLayout(1, 3, 100, 100);
    buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 5));
    JPanel toggles = getJPanelWithGridLayout(2, 1, 70, 20);
    ImageIcon batIcon = new ImageIcon(getClass().getResource("/resources/icons/battery.jpg"));
    Utils.scaleImage(batIcon, 20, 20);
    ImageIcon insIcon = new ImageIcon(getClass().getResource("/resources/icons/insulin.png"));
    Utils.scaleImage(insIcon, 20, 20);
    battery = new JToggleButton("Charge Battery", batIcon);
    battery.addKeyListener(this);
    insulin = new JToggleButton("Fill Insulin        ", insIcon);
    insulin.addKeyListener(this);
    toggles.add(battery);
    toggles.add(insulin);
    buttonPanel.add(toggles);
    buttonPanel.add(new JSeparator(SwingConstants.VERTICAL));
    sendButton = new JButton("Send Event");
    sendButton.addActionListener(this);
    sendButton.setActionCommand("sendbutton");
    sendButton.setPreferredSize(new Dimension(50, 10));
    sendButton.setBorder(new EtchedBorder());
    sendButton.addKeyListener(this);
    buttonPanel.add(sendButton);
    add(buttonPanel);
  }

  private JPanel getTitledBorder(JPanel tbPanel, String tbName)
  {
    tbPanel.setBorder(BorderFactory.createTitledBorder(tbName));
    return tbPanel;
  }

  private JPanel getJPanelWithGridLayout(int rows, int cols, int width, int height)
  {
    GridLayout2 layout = new GridLayout2(rows, cols);
    layout.setVgap(6);
    layout.setHgap(4);
    JPanel panel = new JPanel(layout);
    panel.setPreferredSize(new Dimension(width, height));
    return panel;
  }

  private JTextField getJTextField(String command, String tooltip)
  {
    JTextField jTextField = new JTextField(3);
    jTextField.setDocument(new JTextFieldLimit(3));
    jTextField.setPreferredSize(new Dimension(100, 25));
    jTextField.setFont(new Font("Times New Roman", Font.BOLD, 14));
    jTextField.putClientProperty(command, command);
    jTextField.setToolTipText(tooltip);
    jTextField.addFocusListener(this);
    jTextField.addKeyListener(this);
    textFields.add(jTextField);
    return jTextField;
  }

  private class JTextFieldLimit extends PlainDocument
  {
    private static final long serialVersionUID = 5365545923807798867L;
    private int               limit;

    public JTextFieldLimit(int limit)
    {
      super();
      this.limit = limit;
    }

    public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException
    {
      if (str == null)
      {
        return;
      }

      if ((getLength() + str.length()) <= limit)
      {
        super.insertString(offset, str, attr);
      }
    }
  }

  @Override
  public void actionPerformed(ActionEvent e)
  {
    for (JTextField textField : textFields)
    {
      boolean error = validateInputFields(textField, false);
      if (error)
      {
        sendButton.requestFocus();
        return;
      }
    }
    JButton button = (JButton) e.getSource();
    if (button.getActionCommand().equals("fc"))
    {
      if (pom.getText().equals(""))
      {
        showErrorMessage("Enter a period of measurement first!", "Input needed", JOptionPane.WARNING_MESSAGE);
        return;
      }
      if (timer != null && task != null)
      {
        showErrorMessage("Action not allowed.\nReading from file has already started.", "Invalid Action",
          JOptionPane.WARNING_MESSAGE);
        return;
      }
      int val = fc.showOpenDialog(this);
      if (val == JFileChooser.APPROVE_OPTION)
      {
        file = fc.getSelectedFile();
        if (file != null && !file.getAbsolutePath().endsWith(".xml"))
        {
          showErrorMessage("No XML file selected!", "Invalid File", JOptionPane.ERROR_MESSAGE);
          return;
        }
        fileSelected = true;
        bsvField.setEnabled(false);
        String path = file.getAbsolutePath();
        if (path.length() > 30)
        {
          fcPathLabel.setText(path.substring(0, 30) + "...");
        }
        else
          fcPathLabel.setText(path);
        fcPathLabel.setToolTipText(path);
        return;
      }
      else if (val == JFileChooser.CANCEL_OPTION) return;
    }
    else if (button.getActionCommand().equals("clear"))
    {
      if (timer != null && task != null)
      {
        showErrorMessage("Action not allowed.\nReading from file has already started.", "Invalid Action",
          JOptionPane.WARNING_MESSAGE);
        return;
      }
      resetComponentsOnNoActiveTimer();
      return;
    }
    if (fileSelected)
    {
      if (file != null)
      {
        List<Integer> bsvList = (List<Integer>) parser.parseFile(file.getAbsolutePath());
        if (bsvList == null)
        {
          showErrorMessage("Invalid XML file.\nChoose different one!", "Invalid File", JOptionPane.ERROR_MESSAGE);
          return;
        }
        timer = new java.util.Timer();
        task = new BSLTask(bsvList, insulinPump, timer);
        timer.scheduleAtFixedRate(task, 0, (Integer.parseInt(pom.getText()) * 1000) + 5000);
        fileSelected = false;
      }
      if (task == null || task.isStopped())
      {
        resetComponentsOnNoActiveTimer();
      }
    }

    boolean missing = false;
    StringBuilder message = new StringBuilder("Missing required information:\n");
    for (JTextField textField : textFields)
    {
      if (requiredField(textField))
      {
        if (textField.getClientProperty("pom") != null)
          message.append("Period of Measurement\n");
        else if (textField.getClientProperty("pohc") != null) message.append("Period of Hardware Check\n");
        missing = true;
      }
    }

    if (missing)
    {
      JOptionPane
        .showMessageDialog(this.getParent(), message.toString(), "Missing Values", JOptionPane.WARNING_MESSAGE);
      return;
    }
    controlPumpDevices();
    insulinPump.setBatteryCharged(battery.isSelected());
    insulinPump.setInsulinFilled(insulin.isSelected());
    if (!isSent)
    {
      for (JTextField textField : textFields)
      {
        if (textField.getClientProperty("bsv") == null) textField.setEnabled(false);
      }
      isSent = true;
    }
    HealthStatusController.changeState(1);
  }

  private void resetComponentsOnNoActiveTimer()
  {
    file = null;
    fileSelected = false;
    fcPathLabel.setText("");
    fcPathLabel.setToolTipText("");
    bsvField.setEnabled(true);
    timer = null;
  }

  private boolean requiredField(JTextField textField)
  {
    return textField.getText().equals("") && textField.getClientProperty("bsv") == null;
  }

  private void controlPumpDevices()
  {
    String device = (String) pumpComponents.getSelectedItem();
    int index = pumpComponents.getSelectedIndex();

    if (device.startsWith("Sensor"))
    {
      if (insulinPump.isSensorWorking())
      {
        insulinPump.setSensorWorking(false);
        replaceItem(device, HardwareCheckConstants.HARDWARE_WORKING[index], index);

      }
      else
      {
        insulinPump.setSensorWorking(true);
        replaceItem(device, HardwareCheckConstants.HARDWARE_DEFECTIVE[index], index);
      }
    }
    else if (device.startsWith("Pump"))
    {
      if (insulinPump.isPumpWorking())
      {
        insulinPump.setPumpWorking(false);
        replaceItem(device, HardwareCheckConstants.HARDWARE_WORKING[index], index);
      }
      else
      {
        insulinPump.setPumpWorking(true);
        replaceItem(device, HardwareCheckConstants.HARDWARE_DEFECTIVE[index], index);
      }
    }
    else if (device.startsWith("Needle"))
    {
      if (insulinPump.isNeedleWorking())
      {
        insulinPump.setNeedleWorking(false);
        replaceItem(device, HardwareCheckConstants.HARDWARE_WORKING[index], index);
      }
      else
      {
        insulinPump.setNeedleWorking(true);
        replaceItem(device, HardwareCheckConstants.HARDWARE_DEFECTIVE[index], index);
      }
    }
    else if (device.startsWith("Power"))
    {
      if (insulinPump.isPowerSupplyWorking())
      {
        insulinPump.setPowerSupplyWorking(false);
        replaceItem(device, HardwareCheckConstants.HARDWARE_WORKING[index], index);
      }
      else
      {
        insulinPump.setPowerSupplyWorking(true);
        replaceItem(device, HardwareCheckConstants.HARDWARE_DEFECTIVE[index], index);
      }
    }
    else
      return;

  }

  private void replaceItem(String oldItem, String newItem, int index)
  {
    pumpComponents.removeItem(oldItem);
    pumpComponents.insertItemAt(newItem, index);
    pumpComponents.setSelectedItem(newItem);
  }

  @Override
  public void focusGained(FocusEvent e)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void focusLost(FocusEvent e)
  {
    JTextField jtf = (JTextField) e.getSource();
    validateInputFields(jtf, true);
  }

  private boolean validateInputFields(JTextField jtf, boolean showmessage)
  {
    int num = 0;
    boolean isError = false;
    try
    {
      if (!jtf.getText().equals(""))
      {
        num = Integer.parseInt(jtf.getText());

        if (jtf.getClientProperty("pom") != null)
        {
          if (num >= 20 && num <= 600)
            insulinPump.setMeasurementPeriod(num);
          else
          {
            isError = true;
            if (showmessage) showTfWarningMessage(jtf, "Only numbers from 20 to 600", "Invalid Value");
          }
        }
        else if (jtf.getClientProperty("pohc") != null)
        {
          if (num >= 20 && num <= 600)
            insulinPump.setHardwareCheckPeriod(num);
          else
          {
            isError = true;
            if (showmessage) showTfWarningMessage(jtf, "Only numbers from 20 to 600", "Invalid Value");
          }
        }
        else
        {
          if (num >= 20 && num <= 250)
            insulinPump.setBloodSugarValue(num);
          else
          {
            isError = true;
            if (showmessage) showTfWarningMessage(jtf, "Only numbers from 20 to 250", "Invalid Value");
          }
        }
      }
    }
    catch (NumberFormatException ne)
    {
      isError = true;
      if (showmessage) showTfWarningMessage(jtf, "Only numbers allowed!", "Wrong Input");
    }
    return isError;
  }

  private void showTfWarningMessage(JTextField jtf, String message, String title)
  {
    jtf.requestFocus();
    jtf.setSelectionColor(Color.RED);
    jtf.setSelectionStart(0);
    jtf.setSelectionEnd(jtf.getText().length());
    jtf.removeFocusListener(this);
    JOptionPane.showMessageDialog(this.getParent(), message, title, JOptionPane.WARNING_MESSAGE);
    jtf.addFocusListener(this);
  }

  private void showErrorMessage(String message, String title, int severity)
  {
    JOptionPane.showMessageDialog(this.getParent(), message, title, severity);
  }

  @Override
  public void keyPressed(KeyEvent key)
  {
    if (key.getKeyCode() == KeyEvent.VK_ENTER) sendButton.doClick();
  }

  @Override
  public void keyReleased(KeyEvent arg0)
  {
    // TODO Auto-generated method stub

  }

  @Override
  public void keyTyped(KeyEvent arg0)
  {
    // TODO Auto-generated method stub

  }
}
